﻿using System;
using System.Collections.Generic;
using System.Linq;
using BL.Model.Request;
using Common;
using DAL.Model;

namespace BL
{
    public class TransferController : ITransferController
    {
        private readonly MasterSession _session;
        private readonly DocumentSeqNoHelper _documentSeqNoHelper;

        public TransferController(MasterSession session)
        {
            _session = session;
            _documentSeqNoHelper = new DocumentSeqNoHelper("Transfer", _session);
        }

        public Transfer Create()
        {
            var transfer = new Transfer
                               {
                                   DocumentNo = _documentSeqNoHelper.GetNextDocSeqNo(),
                                   StockedDate = DateTime.Now
                               };
            _session.Transfers.Add(transfer);

            return transfer;
        }

        public void View()
        {
            throw new NotImplementedException();
        }

        public void Update()
        {
            throw new NotImplementedException();
        }

        public void Delete()
        {
            throw new NotImplementedException();
        }

        public void Print()
        {
            throw new NotImplementedException();
        }

        void ITransactionController.Create()
        {
            throw new NotImplementedException();
        }

        public void PrepareSavingData(Transfer transfer)
        {
            if (transfer == null)
            {
                return;
            }
            _documentSeqNoHelper.UpdateDocSeqNo(transfer.DocumentNo);
        }

        public TransferDetail CreateDetail(Transfer transfer)
        {
            var transferDetail = new TransferDetail
                                     {
                                         Transfer = transfer
                                     };
            transfer.TransferDetails.Add(transferDetail);
            _session.TransferDetails.Add(transferDetail);

            return transferDetail;
        }

        public Transfer GenTranferFromStockIns(ICollection<int> stockInIds)
        {
            var stockInWH = _session.Warehouses
                .FirstOrDefault(t => t.Id == Constant.StockOutWarehouseId);

            Transfer transfer = null;
            decimal totalQuantity = 0;
            var stockIns = _session.StockIns
                .Where(t => stockInIds.Contains(t.Id))
                .ToArray();

            var productIds = stockIns.SelectMany(t => t.StockInDetails)
                .Select(t => t.ProductId)
                .ToArray();

            // get all transfer good of products in selected stockins
            var transferMassQtys = _session.StockInsTransfers
                .Select(t => t.Transfer)
                .SelectMany(t => t.TransferDetails)
                .Where(t => productIds.Contains(t.ProductId))
                .GroupBy(t => t.ProductId)
                .ToDictionary(t => t.Key, t => t.Sum(v => v.MassQuantity));

            // get all return good of products in selected stockins
            var returnMassQtys = _session.StockOutDetails
                .Where(t => (t.TransferDetail == null || (bool) t.IsReturnGood) &&
                            productIds.Contains(t.ProductId))
                .GroupBy(t => t.ProductId)
                .ToDictionary(t => t.Key, t => t.Sum(v => v.MassQuantity));

            foreach(var stockIn in stockIns)
            {
                foreach (var stockInDetail in stockIn.StockInDetails)
                {
                    // check for out of stock product
                    var transferMassQty = transferMassQtys
                        .FirstOrDefault(t => t.Key == stockInDetail.ProductId)
                        .Value;
                    var returnMassQty = returnMassQtys
                        .FirstOrDefault(t => t.Key == stockInDetail.ProductId)
                        .Value;

                    if ((stockInDetail.StockOutMassQuantity >= stockInDetail.Quantity) ||   // is out of stock
                        (transferMassQty + returnMassQty >= stockInDetail.Quantity))        // total tranfer and return > stockin
                    {
                        // do not transfer out-of-stock product
                        continue;
                    }

                    // if there is return-goods from stockout
                    var massQuantity = transferMassQty + returnMassQty < stockInDetail.StockOutMassQuantity
                                       ? stockInDetail.Quantity - stockInDetail.StockOutMassQuantity
                                       : stockInDetail.Quantity - transferMassQty - returnMassQty;
                    if (massQuantity <= 0)
                    {
                        continue;
                    }
                    if (transfer == null)
                    {
                        // create transfer header
                        transfer = Create();

                        transfer.StockIn = stockIn;
                        transfer.Customer = stockIn.Customer ?? stockIn.Merchant;
                        transfer.Warehouse = stockIn.Warehouse;
                        transfer.Warehouse1 = stockInWH;
                        transfer.ContactName = stockIn.Customer != null
                                                   ? stockIn.Customer.ContactName
                                                   : string.Empty;
                        transfer.StockedDate = DateTime.Now;
                    }

                    // create transfer details
                    var transferDetail = CreateDetail(transfer);
                    transferDetail.StockInDetail = stockInDetail;
                    transferDetail.Product = stockInDetail.Product;
                    transferDetail.StockInQuantity = stockInDetail.Quantity;
                    transferDetail.MassQuantity = massQuantity;
                    transferDetail.UnitOfMeasure = stockInDetail.UnitOfMeasure;

                    totalQuantity += transferDetail.Quantity;
                    transfer.TotalQuantity = totalQuantity;
                }

                // trace transfer by stockin
                if (transfer == null)
                {
                    continue;
                }
                var stockInsTransfer = new StockInsTransfer
                {
                    StockIn = stockIn,
                    Transfer = transfer
                };
                _session.StockInsTransfers.Add(stockInsTransfer);
                transfer.StockInsTransfers.Add(stockInsTransfer);
            }

            return transfer;
        }

        /// <summary>
        /// Search Transfer by keyword with ordering
        /// </summary>
        /// <param name="request"></param>
        /// <returns>List of Transfer</returns>
        /// <history>
        /// 12/30/2014 phat.vo: Create new
        /// </history>
        public List<Transfer> Search(SearchingTransferRequest request)
        {
            var isAllDocumentNo = string.IsNullOrWhiteSpace(request.DocumentNo);
            var documentNo = isAllDocumentNo ? string.Empty : request.DocumentNo.Trim().ToLower();

            var isAllCustomer = request.CustomerIds == null || request.CustomerIds.Contains(Constant.AllValue);
            var customerIds = isAllCustomer ? new List<int>() : request.CustomerIds;

            var isAllStockInNo = string.IsNullOrWhiteSpace(request.StockInNo);
            var stockInNo = isAllStockInNo ? string.Empty : request.StockInNo.Trim().ToLower();

            var isAllProductCode = string.IsNullOrWhiteSpace(request.ProductCode);
            var productCode = isAllProductCode ? string.Empty : request.ProductCode.Trim().ToLower();

            var query =
                (from transfer in _session.Transfers
                 join transferDetail in _session.TransferDetails on transfer.Id equals transferDetail.TransferId
                 where (isAllDocumentNo || transfer.DocumentNo.ToLower() == documentNo.ToLower()) &&
                       (isAllStockInNo || transfer.StockIn.DocumentNo.ToLower() == stockInNo.ToLower()) &&
                       (!request.FromTime.HasValue || request.FromTime <= transfer.StockedDate) &&
                       (!request.ToTime.HasValue || request.ToTime >= transfer.StockedDate) &&
                       (isAllCustomer || customerIds.Contains(transfer.CustomerId)) &&
                       (isAllProductCode || transfer.TransferDetails
                                                .Any(t => t.Product.Code == productCode))  &&
                       (request.ProductCategoryId < 0 || (transferDetail.Product.ProductCategoryId == request.ProductCategoryId))
                 select transfer)
                    .OrderByDescending(t => t.StockedDate);

            return query.ToList();
        }
    }
}
